<?php
/**
 * *********************************************************************************************************************
 * 支付宝服务窗
 * https://doc.open.alipay.com/doc2/detail?treeId=53&articleId=103245&docType=1
 *
 *  公共请求参数
 *
 *  REQUEST URL: https://openapi.alipay.com/gateway.do
 *  REQUEST METHOD: POST
 *  CONTENT:
 *  app_id=2015051800078634
 *  method=alipay.mobile.public.message.matcher.send
 *  charset=GBK
 *  sign_type=RSA
 *  timestamp=2014-07-24 03:07:50
 *  biz_content={"matcher": {"mobileNo": "15800000000"},"template":{"templateId":"cbb9fdded2b74fe4bb082de3ed1e1eb2","context":{"headColor":"#85be53","url":"http://m.baidu.com","keyword1":{"color":"#000000","value":"2014-09-24"},"keyword2":{"color":"#85be53","value":"HU7142"}}}}
 *  sign=e9zEAe4TTQ4LPLQvETPoLGXTiURcxiAKfMVQ6Hrrsx2hmyIEGvSfAQzbLxHrhyZ48wOJXTsD4FPnt+YGdK57+fP1BCbf9rIVycfjhYCqlFhbTu9pFnZgT55W+xbAFb9y7vL0MyAxwXUXvZtQVqEwW7pURtKilbcBTEW7TAxzgro=version=1.0
 *
 *  签名
 *  sign 中的data是上面发送内容除去sign的字典排序
 *  例如：app_id=2014072300007148&biz_content={"matcher": {"mobileNo": "15800000000"},"template":{"templateId":"cbb9fdded2b74fe4bb082de3ed1e1eb2","context":{"headColor":"#85be53","url":"http://m.baidu.com","keyword1":{"color":"#000000","value":"2014-09-24"},"keyword2":{"color":"#85be53","value":"HU7142"}}}}&charset=GBK&method=alipay.mobile.public.menu.add&sign_type=RSA&timestamp=2014-07-24 03:07:50&version=1.0
 *
 *  返回码               说明
 *
 *  200                 成功
 *  12036               已成功，该消息待⽤用户确认接受中
 *  503                 系统错误
 *  1001                ⽆无效的业务参数
 *  2002                公众账号暂不能提供服务
 *  2005                ⽆无效的⼿手机号mobileNo!!!
 *  12006               单发消息频率超限
 *  12007               没有关注者，发送消息失败
 *  12020               消息模板不存在
 *  12021               消息模板未启⽤用
 *  12022               ⽆无效的消息模板标识templateId!!!
 *  12023               ⽆无效的消息模板内容template!!!
 *  12024               ⽆无效的消息模板KEY，和预设变量KEY冲突!!!
 *  12025               ⽆无效的匹配器matcher!!!
 *  12026               该模板第x⾏行,第y列的变量"z"没有替换值!!!
 *  12027               未匹配到对应⽀支付宝账户(account)
 *  12028               未匹配到对应⽀支付宝账户（app）
 *  12029               未匹配到对应⽀支付宝账户（active）
 *  12030               ⽤用户拒绝接受该服务窗消息
 *  12037               匹配模板授权超限
 *  12038               匹配模板授权,未找到授权模板
 *  14004               添加关注失败
 *
 * 返回值
 *  {
 *      "alipay_mobile_public_message_matcher_send_response": {
 *          "code": "200",
 *          "msg": "成功",
 *          "to_user_id": "RaUdyYfSE7PErNQjhpBYERuSX3JjLZtwilbxOzPGDUrbq9FV8CBamV+6ZPDwOg7401",
 *          },
 *      "sign": "qnBYjFwNERA9JMtdewqy8Cxmdt+16D5Sh8JEf9EAJ8EAZ24QVR9zZl8ceMmP2/RUZ9kAxRYPWbPENfXAadv79RwxxIrXP3EOJVbazREw5AsP13muAFm3aryIQN7Xclj84//9maEhtSDYK+JWQyhAE7+B4oOUkNdwqEEKT8HVerA="
 * }
 * *********************************************************************************************************************
 */
namespace soa\message\alipayServiceWindow;
use artisan\http;

class serviceWindow
{
    /**
     * 服务地址
     * @var string
     */
    public $service_url = 'https://openapi.alipay.com/gateway.do';
    /**
     * 服务项目名称
     * @var string
     */
    public $service_name = 'alipay';

    /**
     * 私钥PEM文件地址
     * @var string
     */
    private $rsaPrivateKey = '/kuaibao/www/kdhelp/soa/message/alipayServiceWindow/rsa_private_key.pem';
    //private $rsaPrivateKey = 'D:\www\kdhelp\soa\message\alipayServiceWindow\rsa_private_key.pem';

    /**
     * 支付宝服务窗交互接口
     * @param $appId
     * @param $method
     * @param array $fields
     * @param string $version
     * @return bool|array|string
     */
    protected function send($appId, $method, array $fields, $version='1.0')
    {

        $packageSendData = [
            'app_id' => $appId,
            'method' => $method,
            'charset' => 'UTF-8',
            'sign_type' => 'RSA',
            'timestamp' => date('Y-m-d H:i:s'),
            'version' => $version
        ];

        foreach ($fields as $field => $biz_content) {
            $packageSendData[$field] = is_array($biz_content) ? json_encode($biz_content) : $biz_content;
        }

        $packageSignData = $this->packageSignData($packageSendData);
        $sign = $this->sign($packageSignData, $this->getRsaPrivateKey());
        if(empty($sign)) {
            return false;
        }

        $packageSendData['sign'] = $sign;


        $packageSendData = array_filter($packageSendData);

        $retJsonGbk = http::post($this->service_url, $packageSendData);
        if(empty($retJsonGbk)) {
            return false;
        }

        $fieldName = str_replace('.', '_', $method) . '_response';
        //alipay 真是坑爹，为何喜欢不用utf8的编码
        $retJsonUtf8 = mb_convert_encoding($retJsonGbk, 'UTF-8', 'GBK');
        //json 转 array
        $retArr = json_decode($retJsonUtf8, true);
        //处理返回的数据
        if(json_last_error() ==0 && isset($retArr[$fieldName])) {
            return $retArr[$fieldName];
        } else {
            if(isset($retArr['error_response'])) {
                $msg = 'code:' . $retArr['error_response']['code'].', msg:'. $retArr['error_response']['msg'];
                if(isset($retArr['error_response']['sub_code']))
                    $msg .= '|sub_code:' . $retArr['error_response']['sub_code'] . ', sub_msg:' . $retArr['error_response']['sub_msg'];

                logDebug($msg, $fieldName);
                return $retArr['error_response']['msg'].'-'.$retArr['error_response']['sub_msg'];
            }
        }

        return false;
    }

    /**
     * 签名中用的数据包装
     * @param array $signArr
     * @return string
     */
    private function packageSignData(array $signArr)
    {
        ksort($signArr);
        $temp = array_filter($signArr);
        if(!empty($temp))
            return urldecode(http_build_query($temp));
        else
            return '';
    }

    /**
     * 支付宝签名
     * @param $data
     * @param $rsaPrivateKey
     * @return string
     */
    private function sign($data, $rsaPrivateKey)
    {
        if(!file_exists($rsaPrivateKey))
            return '';


        if(!extension_loaded('openssl'))
            return '';

        //获取私钥PEM文件内容，$rsaPrivateKey是指向私钥PEM文件的路径
        $priKey = file_get_contents($rsaPrivateKey);
        // 从PEM文件中提取私钥
        $res = openssl_pkey_get_private($priKey);

        if(is_resource($res)) {
            //对数据进行签名
            openssl_sign($data, $sign, $res);
            // 释放资源
            openssl_free_key($res);
            // 对签名进行Base64编码，变为可读的字符串
            $sign = base64_encode($sign);

            return $sign;
        } else {
            return '';
        }
    }

    public function getRsaPrivateKey()
    {
        return $this->rsaPrivateKey;
    }
}